/*****************************************************************************/
/*************** Implementation of the Buffer Manager Layer ******************/
/*****************************************************************************/

#include "buf.h"

// Define buffer manager error messages here
//enum bufErrCodes  {...};

// Define error message here
static const char* bufErrMsgs[] = {
// error message strings go here
		};

// Create a static "error_string_table" object and register the error messages
// with minibase system
static error_string_table bufTable(BUFMGR, bufErrMsgs);

BufMgr::BufMgr(int numbuf, Replacer *replacer) {
	// put your code here
	this->numBuffers = numbuf;
	this->replacer = replacer;

	this->frmeTable = new FrameDesc[numbuf];
	this->bufPool = new Page[numbuf];
}

Status BufMgr::pinPage(PageId PageId_in_a_DB, Page*& page, int emptyPage) {
	// look up in the pool
	map<int, int>::iterator iter = hash_table.find(PageId_in_a_DB);
	if (iter != hash_table.end()) {
		int frame_num = iter->second;
		frmeTable[frame_num].pin();
		*page = bufPool[frame_num];
		return OK;
	}

	// not in the pool, find an available frame
	int victim_frame = this->pick_victim();
	int victim_page = frmeTable[victim_frame].getPageNo();

	// load the page from disk
	flushPage(victim_page);
	Status status = MINIBASE_DB->read_page(PageId_in_a_DB,
			&bufPool[victim_frame]);
	if (status != OK) {
		return status;
	}
	*page = bufPool[victim_frame];

	// update frmeTable
	bool dirty = (emptyPage == 0) ? false : true;
	frmeTable[victim_frame].resetFrameDesc(PageId_in_a_DB, victim_frame, dirty);


	map<int, int>::iterator start = hash_table.begin();
	while (start != hash_table.end()) {
		cout << start->first << endl;
		start++;
	}


	// update hashTable
	if (victim_page != INVALID_PAGE)
		hash_table.erase(hash_table.find(victim_page));
	hash_table.insert( make_pair(PageId_in_a_DB, victim_frame) );
//	cout << hash_table[PageId_in_a_DB] << endl;

	return OK;
}

Status BufMgr::newPage(PageId& firstPageId, Page*& firstpage, int howmany) {
	// put your code here
	return OK;
}

Status BufMgr::flushPage(PageId pageid) {
	if (pageid != INVALID_PAGE) {
		map<int, int>::iterator iter = hash_table.find(pageid);
		assert(iter != hash_table.end());
		int frame_num = iter->second;
		if (frmeTable[frame_num].getDirtybit())
			MINIBASE_DB->write_page(frmeTable[frame_num].getPageNo(),
					&bufPool[frame_num]);
	}

	return OK;
}

//*************************************************************
//** This is the implementation of ~BufMgr
//************************************************************
BufMgr::~BufMgr() {
	// put your code here
	delete this->frmeTable;
	delete this->bufPool;
}

//*************************************************************
//** This is the implementation of unpinPage
//************************************************************

Status BufMgr::unpinPage(PageId page_num, int dirty /*FALSE*/, int hate /*FALSE*/) {
	// look it up in the pool
	map<int, int>::iterator iter = hash_table.find(page_num);
	assert(iter != hash_table.end());

	// change dirty bit and pin_count
	int frame_num = iter->second;
	frmeTable[frame_num].setDirty(dirty);
	frmeTable[frame_num].unpin();

	// see if it should be written back to disk
	if (frmeTable[frame_num].pin_count() == 0
			&& frmeTable[frame_num].getDirtybit() == 1) {
		MINIBASE_DB->write_page(page_num, &bufPool[frame_num]);
//		frmeTable[frame_num].resetFrameDesc()
	}

	return OK;
}

//*************************************************************
//** This is the implementation of freePage
//************************************************************

Status BufMgr::freePage(PageId globalPageId) {
	// put your code here
	return OK;
}

Status BufMgr::flushAllPages() {
	//put your code here
	return OK;
}

//*************************************************************
//** This is the implementation of replacement algorithm
//************************************************************

int BufMgr::pick_victim() {
	int victim = 0;
	for (int i = 0; i < (int)numBuffers; ++i) {
		if (frmeTable[i].frameNo == INVALID_PAGE) {
			victim = i;
			break;
		}
	}

	return victim;
}

